import type { Map } from "ol";
import type { FeatureLike } from "ol/Feature";
import type { GeoJSONFeature } from "ol/format/GeoJSON";
import type { Geometry } from "ol/geom";
import type { DrawEvent } from "ol/interaction/Draw";
import type { ModifyEvent } from "ol/interaction/Modify";
import type RenderFeature from "ol/render/Feature";
import GeoJSON from "ol/format/GeoJSON";
import { Point } from "ol/geom";
import { Draw, Modify, Snap } from "ol/interaction";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import { getArea, getLength } from "ol/sphere";
import { Circle as CircleStyle, Fill, Stroke, Style, Text } from "ol/style";
import { ref, toRaw } from "vue";
import { projection } from "./view";

// 测量相关变量
let measureSource: VectorSource | null = null; // 测量图层数据源
let measureLayer: VectorLayer<VectorSource> | null = null; // 测量图层
let draw: Draw | null = null; // 绘制交互
let modify: Modify | null = null; // 修改交互
let snap: Snap | null = null; // 捕捉交互
let map: Map | null = null; // 当前地图实例
let geoJson: GeoJSONFeature | null = null; // 绘制的边界GeoJSON, 用于编辑

// vue页面更新状态
export const measureType = ref<"length" | "area" | null>(null); // 当前测量类型
export const isMeasuring = ref<boolean>(false); // 是否正在测量

/** 重置 */
const reset = (): void => {
    if (map && measureLayer) map.removeLayer(measureLayer);
    isMeasuring.value = false;
    measureType.value = null;
    geoJson = null;
    measureSource = null;
    measureLayer = null;
    draw = null;
    modify = null;
    snap = null;
    map = null;
};

/** 初始化测量图层 */
export const initMeasure = (_map: Map): void => {
    reset();
    map = toRaw(_map); // Note[vue] 获取原始地图实例, 否则添加交互后会生成代理对象, 和原交互不是一个对象, 不能正确移除交互
    measureLayer = new VectorLayer({
        source: new VectorSource(),
        zIndex: 1000, // 确保测量图层在最上层
        style: feature => createMeasureStyle(feature, false),
    });
    map.addLayer(measureLayer);
    measureSource = measureLayer.getSource();
};

/** 开始测量 */
export const startMeasure = (type: "length" | "area"): void => {
    if (!map || !measureSource) {
        console.error("地图或测量数据源未初始化");
        return;
    }

    geoJson = null; // 清除之前的GeoJSON结果
    // 停止之前的测量
    stopMeasure();

    measureType.value = type;
    isMeasuring.value = true;

    // 创建绘制交互
    draw = new Draw({
        source: measureSource,
        type: type === "length" ? "LineString" : "Polygon",
        style: feature => createMeasureStyle(feature, true),
    });

    draw.on("drawend", onDrawEnd); // 监听绘制完成事件

    // 添加交互到地图
    map.addInteraction(draw);
};

/** 绘制完成回调 */
const onDrawEnd = (event: DrawEvent): void => {
    const feature = event.feature;
    const geometry = feature.getGeometry();
    if (!geometry) return;
    feature.set("measurement", getMeasurement(geometry)); // 添加测量结果到要素属性
    stopMeasure();
};

/** 修改绘制好的测量 */
export const editMeasure = (): void => {
    if (!map || !measureSource) {
        console.error("地图或测量数据源未初始化");
        return;
    }

    geoJson = null; // 清除之前的GeoJSON结果
    isMeasuring.value = true;

    // 添加修改交互, 可以拖拽线条进行修改
    modify = new Modify({ source: measureSource });
    // 监听修改完成事件
    modify.on("modifyend", (event: ModifyEvent): void => {
        const feature = event.features.getArray()[0];
        const geometry = feature.getGeometry();
        if (geometry) {
            // 通过类型简单判断一下是否改变的是边界绘制
            if (geometry.getType() === "MultiPolygon") {
                geoJson = new GeoJSON().writeFeatureObject(feature);
            } else {
                feature.set("measurement", getMeasurement(geometry, true)); // 重新计算测量结果
            }
        }
    });
    map.addInteraction(modify);

    // 添加捕捉交互, 拖拽时将点吸附到已有的线或多边形上
    snap = new Snap({ source: measureSource });
    map.addInteraction(snap);
};

/** 绘制边界 */
export const drawBoundary = (): Promise<GeoJSONFeature> => {
    return new Promise((resolve) => {
        if (!map || !measureSource) {
            console.error("地图或测量数据源未初始化");
            return;
        }

        clearMeasure(); // 清除之前的测量结果, 不允许同时绘制多个边界

        isMeasuring.value = true;

        // 创建绘制交互
        draw = new Draw({
            source: measureSource,
            type: "MultiPolygon",
        });

        draw.on("drawend", (event: DrawEvent) => {
            stopMeasure();
            // const clonedFeature = event.feature.clone(); // 克隆一个要素进行坐标转换, 避免修改原始要素
            // clonedFeature.getGeometry()?.transform(projection, locationProjection); // 转换坐标系
            const geoJson = new GeoJSON().writeFeatureObject(event.feature);
            resolve(geoJson);
        });

        // 添加交互到地图
        map.addInteraction(draw);
    });
};

/** 停止测量 */
export const stopMeasure = (): Promise<GeoJSONFeature | null> => {
    if (!map) {
        console.error("地图未初始化");
        return Promise.resolve(null);
    }

    // 移除交互
    if (draw) {
        map.removeInteraction(draw);
        draw = null;
    }
    if (modify) {
        map.removeInteraction(modify);
        modify = null;
    }
    if (snap) {
        map.removeInteraction(snap);
        snap = null;
    }

    // 重置状态
    measureType.value = null;
    isMeasuring.value = false;

    return Promise.resolve(geoJson); // 返回绘制的边界GeoJSON
};

/** 清除测量结果 */
export const clearMeasure = (): void => {
    geoJson = null; // 清除之前的GeoJSON结果
    stopMeasure();
    measureSource?.clear();
};

/** 动态测量样式函数 */
const createMeasureStyle = (feature: FeatureLike, isDrawing = false): Style[] => {
    const strokeColor = isDrawing ? "#ff6b6b" : "#ffcc33"; // 绘制时红色，完成后黄色
    const styles: Style[] = [new Style({
        fill: new Fill({
            color: "rgba(255, 204, 51, 0.2)",
        }),
        stroke: new Stroke({
            color: strokeColor,
            width: 2,
        }),
        image: new CircleStyle({
            radius: 7,
            fill: new Fill({
                color: strokeColor,
            }),
        }),
    })];

    const geometry = feature.getGeometry();
    if (!geometry) return styles;

    // 获取绘制存储的测量结果
    let measurement = feature.get("measurement");
    // 如果正在绘制，则获取实时测量结果
    if (isDrawing) measurement = getMeasurement(geometry);

    let labelPoint: number[] | undefined;
    let offsetY = -15; // 默认偏移量

    if (geometry.getType() === "LineString") {
        // 对于线段，标签显示在中点，并且距离线更远
        const coordinates = (geometry as any).getCoordinates();
        labelPoint = coordinates[coordinates.length - 1]; // 使用最后一个点作为标签位置
        offsetY = -25; // 距离线段更远
    } else if (geometry.getType() === "Polygon") {
        // 对于多边形，标签显示在重心
        labelPoint = (geometry as any).getInteriorPoint().getCoordinates();
        offsetY = -15; // 多边形保持原来的偏移
    }

    if (labelPoint) {
        const labelStyle = new Style({
            geometry: new Point(labelPoint),
            text: new Text({
                text: measurement,
                fill: new Fill({ color: "#000" }),
                stroke: new Stroke({ color: "#fff", width: 3 }),
                font: "bold 14px Arial",
                offsetY,
                backgroundFill: new Fill({ color: "rgba(255, 255, 255, 0.9)" }),
                backgroundStroke: new Stroke({ color: strokeColor, width: 1 }),
                padding: [5, 6, 4, 8],
            }),
        });
        styles.push(labelStyle);
    }

    return styles;
};

/** 获取测量结果 */
const getMeasurement = (geometry: Geometry | RenderFeature, ignoreMeasure: boolean = false): string => {
    if (!ignoreMeasure && !measureType.value) {
        console.error("测量类型未设置");
        return "";
    }
    let measurement = "";
    const geometryType = geometry.getType();
    if (geometryType === "LineString" && (ignoreMeasure || measureType.value === "length")) {
        const length = getLength(geometry as any, { projection });
        measurement = formatLength(length);
    } else if (geometryType === "Polygon" && (ignoreMeasure || measureType.value === "area")) {
        const area = getArea(geometry as any, { projection });
        measurement = formatArea(area);
    }
    return measurement;
};

/** 格式化长度 */
const formatLength = (length: number): string => {
    if (length > 1000) {
        return `${Math.round((length / 1000) * 100) / 100} km`;
    } else {
        return `${Math.round(length * 100) / 100} m`;
    }
};

/** 格式化面积 */
const formatArea = (area: number): string => {
    if (area > 1000000) {
        return `${Math.round((area / 1000000) * 100) / 100} km²`;
    } else {
        return `${Math.round(area * 100) / 100} m²`;
    }
};

/** 清理测量模块资源 */
export const destroyMeasure = (): void => {
    clearMeasure(); // 清除测量结果
    reset(); // 重置所有状态
};
